package org.zoomdev.zoom.web.router;

import org.zoomdev.zoom.web.action.ActionHandler;

import java.util.Map;

public final class ReadOnlyRouterMap {
    private final Bucket[] _buckets;

    private final int _size;

    private final int _mask;

    public ReadOnlyRouterMap(Map<String, ActionHandler[]> map) {
        int size = findSize(map.size());
        _size = size;
        _mask = (size - 1);
        Bucket[] buckets = new Bucket[size];
        for (Map.Entry<String, ActionHandler[]> entry : map.entrySet()) {
            String key = entry.getKey();
            int index = key.hashCode() & _mask;
            buckets[index] = new Bucket(buckets[index], key, entry.getValue());
        }
        _buckets = buckets;
    }

    private static int findSize(int size) {
        // For small enough results (64 or less), we'll require <= 50% fill rate; otherwise 80%
        int needed = (size <= 64) ? (size + size) : (size + size);
        int result = 8;
        while (result < needed) {
            result += result;
        }
        return result;
    }

    private final static class Bucket {
        public final ActionHandler[] value;
        public final Bucket next;

        public final String key;

        public Bucket(Bucket next, String key, ActionHandler[] value) {
            this.next = next;
            this.value = value;
            this.key = key;
        }


    }

    public ActionHandler[] find(String key) {
        int index = key.hashCode() & _mask;
        Bucket bucket = _buckets[index];
        // Let's unroll first lookup since that is null or match in 90+% cases
        if (bucket == null) {
            return null;
        }
        // Primarily we do just identity comparison as keys should be interned
        if (key.equals(bucket.key)) {
            return bucket.value;
        }
        while ((bucket = bucket.next) != null) {
            if (key.equals(bucket.key)) {
                return bucket.value;
            }
        }
        return null;
    }
}
